using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using GameStateManagement;
using Frogger_Input;

// ****************************************************************************** //
//       This component handle all the options menu entries and functions         //
// ****************************************************************************** //


namespace Frogger_Game
{
    /// <summary>
    /// Questo è un componente del gioco che implementa IUpdateable.
    /// </summary>
    public class Options_Menu : Microsoft.Xna.Framework.DrawableGameComponent
    {
        public Options_Menu(Game game)
            : base(game)
        {
        }

        #region Fields
         
        //graphics assets
        public Texture2D background;
        SpriteBatch spriteBatch;
        SpriteFont font;

        //components
        MessageBoxScreen messageBox;
        SoundData sound;

        //menu & input elements
        List<MenuEntry> menuEntries;
        List<MenuEntry> menuArrows;
        int selectedEntry = 0;
        int selectedArrow = 0;

        MenuEntry leftArrow;
        MenuEntry rightArrow;
        MenuEntry minMusicVolume;
        MenuEntry maxMusicVolume;
        MenuEntry minEffectsVolume;
        MenuEntry maxEffectsVolume;
        MenuEntry applyRes;
        MenuEntry fullScreenMenuEntry;
        MenuEntry resolutionMenuEntry;
        MenuEntry musicVolumeMenuEntry;
        MenuEntry effectsVolumeMenuEntry;

        InputState input;
        InputButtons ib;

        //graphic device variables for resolution changing
        GraphicsDeviceManager graphics;
        DisplayModeCollection supported_res;
        DisplayMode[] res;
        int currentRes = 0;

        #endregion


        /// <summary>
        /// Initialize all needed variables
        /// </summary>
        public override void Initialize()
        {
            font = Game.Content.Load<SpriteFont>("menufont");
            
            //get input services
            input = (InputState)Game.Services.GetService(typeof(InputState));
            ib = (InputButtons)Game.Services.GetService(typeof(InputButtons));
            graphics = (GraphicsDeviceManager)Game.Services.GetService(typeof(GraphicsDeviceManager));

            menuEntries = new List<MenuEntry>();
            menuArrows = new List<MenuEntry>();

            //create menu entries
            resolutionMenuEntry = new MenuEntry(string.Empty, font);
            fullScreenMenuEntry = new MenuEntry(string.Empty, font);
            musicVolumeMenuEntry = new MenuEntry(string.Empty, font);
            effectsVolumeMenuEntry = new MenuEntry(string.Empty, font);

            MenuEntry back = new MenuEntry("Back",font);
            leftArrow = new MenuEntry("-", font);
            rightArrow = new MenuEntry("+", font);
            minMusicVolume = new MenuEntry("-", font);
            maxMusicVolume = new MenuEntry("+", font);
            minEffectsVolume = new MenuEntry("-", font);
            maxEffectsVolume = new MenuEntry("+", font);
           
            // Hook up menu event handlers.
            leftArrow.Selected += LeftResMenuEntrySelected;
            rightArrow.Selected += RightResMenuEntrySelected;
            minMusicVolume.Selected += MusicVolumeDownEntrySelected;
            maxMusicVolume.Selected += MusicVolumeUpEntrySelected;
            minEffectsVolume.Selected += EffectsVolumeDownEntrySelected;
            maxEffectsVolume.Selected += EffectsVolumeUpEntrySelected;
#if WINDOWS 
            fullScreenMenuEntry.Selected += FullScreenEntrySelected;
#endif
            back.Selected += BackSelected;

            // Add entries to the menu.
            menuEntries.Add(resolutionMenuEntry);
            menuEntries.Add(fullScreenMenuEntry);
            menuEntries.Add(musicVolumeMenuEntry);
            menuEntries.Add(effectsVolumeMenuEntry);
            menuEntries.Add(back);
            menuArrows.Add(leftArrow);
            menuArrows.Add(rightArrow);
            menuArrows.Add(minMusicVolume);
            menuArrows.Add(maxMusicVolume);
            menuArrows.Add(minEffectsVolume);
            menuArrows.Add(maxEffectsVolume);

           // SetMenuEntryText();

            //Set sound volumes
            sound = (SoundData)Game.Services.GetService(typeof(SoundData));
            sound.SetMusicVolume(Game1.musicVolume);
            sound.SetEffectsVolume(Game1.effectsVolume);
            base.Initialize();
        }

        #region Handle Input

        /// <summary>
        /// Event handler for when the Back menu entry is selected.
        /// </summary>
        void BackSelected(object sender, PlayerIndexEventArgs e)
        {

            while (Game.Components.Count > 1)
            {
                ((GameComponent)Game.Components[1]).Dispose();
            }

            Game.Components.Add(new Main_Menu(Game));
        }

        /// <summary>
        /// Event handler for when the right arrow in Resolution menu entry is selected.
        /// </summary>
        void RightResMenuEntrySelected(object sender, PlayerIndexEventArgs e)
        {
            currentRes = (currentRes + 1);
            if (currentRes == (res.Length)) currentRes = res.Length - 1;
            SetMenuEntryText();
            graphics.PreferredBackBufferWidth = res[currentRes].Width;
            graphics.PreferredBackBufferHeight = res[currentRes].Height;
            if (menuEntries.Count < 6)
            {
            applyRes = new MenuEntry("Save resolution changes", font);
            applyRes.Selected += ApplyMenuEntrySelected;
            menuEntries.Insert(1, applyRes);

            }
        }

        /// <summary>
        /// Event handler for when the left arrow in Resolution menu entry is selected.
        /// </summary>
        void LeftResMenuEntrySelected(object sender, PlayerIndexEventArgs e)
        {
            currentRes = (currentRes - 1);
            if (currentRes == (-1)) currentRes = 0;
            SetMenuEntryText();
            graphics.PreferredBackBufferWidth = res[currentRes].Width;
            graphics.PreferredBackBufferHeight = res[currentRes].Height;
            if (menuEntries.Count < 6)
            {

                applyRes = new MenuEntry("Save resolution changes", font);
                applyRes.Selected += ApplyMenuEntrySelected;
                menuEntries.Insert(1, applyRes);

            }

        }


        /// <summary>
        /// Event handler for when the Apply Resolution menu entry is selected.
        /// </summary>
        void ApplyMenuEntrySelected(object sender, PlayerIndexEventArgs e)
        {
            messageBox = new MessageBoxScreen(Game, "Are you sure you want to apply changes?", true);
            Game.Components.Add(messageBox);
            messageBox.Accepted += ConfirmGraphicsChanges;
            messageBox.Cancelled += DiscardGraphicsChanges;
        }

        /// <summary>
        /// Event handler for when the Apply Resolution messagebox is cancelled.
        /// </summary>
        void DiscardGraphicsChanges(object sender, PlayerIndexEventArgs e)
        {

            messageBox.Dispose();
            Game.Components.Remove(messageBox);
            while (Game.Components.Count > 1)
            {
                ((GameComponent)Game.Components[1]).Dispose();
            }

            Game.Components.Add(new Options_Menu(Game));

        }

        /// <summary>
        /// Event handler for when the Apply Resolution messagebox is accepted.
        /// </summary>
        void ConfirmGraphicsChanges(object sender, PlayerIndexEventArgs e)
        {

            messageBox.Dispose();
            Game.Components.Remove(messageBox);
            graphics.ApplyChanges();
            MapGrid.getScreenRatio(graphics.GraphicsDevice.Viewport.AspectRatio);
            while (Game.Components.Count > 1)
            {
                ((GameComponent)Game.Components[1]).Dispose();
            }

            Game.Components.Add(new Options_Menu(Game));

        }


        /// <summary>
        /// Event handler for when the full screen entry is selected.
        /// </summary>
        void FullScreenEntrySelected(object sender, PlayerIndexEventArgs e)
        {
            graphics.ToggleFullScreen();
            SetMenuEntryText();
        }

        /// <summary>
        /// Event handler for when the music volume down menu entry is selected.
        /// </summary>
        void MusicVolumeDownEntrySelected(object sender, PlayerIndexEventArgs e)
        {
            if (Game1.musicVolume > 0)
            {
                Game1.musicVolume--;
                SetMenuEntryText();
                sound.MusicVolumeDown(Game1.musicVolume);
            }

        }

        /// <summary>
        /// Event handler for when the music volume up menu entry is selected.
        /// </summary>
        void MusicVolumeUpEntrySelected(object sender, PlayerIndexEventArgs e)
        {
            if (Game1.musicVolume < 10)
            {
                Game1.musicVolume++;
                SetMenuEntryText();
                sound.MusicVolumeUp(Game1.musicVolume);
            }
        }

        /// <summary>
        /// Event handler for when the effects volume down menu entry is selected.
        /// </summary>
        void EffectsVolumeDownEntrySelected(object sender, PlayerIndexEventArgs e)
        {
            if (Game1.effectsVolume > 0)
            {
                Game1.effectsVolume--;
                SetMenuEntryText();
                sound.EffectsVolumeDown(Game1.effectsVolume);
            }
            sound.PlaySound(sound.HopSoundInstance);

        }

        /// <summary>
        /// Event handler for when the effects volume up menu entry is selected.
        /// </summary>
        void EffectsVolumeUpEntrySelected(object sender, PlayerIndexEventArgs e)
        {
            if (Game1.effectsVolume < 10)
            {
                Game1.effectsVolume++;
                SetMenuEntryText();
                sound.EffectsVolumeUp(Game1.effectsVolume);
            }
            sound.PlaySound(sound.HopSoundInstance);
        }

        #endregion

        /// <summary>
        /// Load graphics content
        /// </summary>
        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);
            background = Game.Content.Load<Texture2D>("menu_back");
            
            //load all resolutions supported by the graphics device (video card & monitor)
            supported_res = this.GraphicsDevice.Adapter.SupportedDisplayModes;
            res = supported_res.ToArray();
            for (int i = 0; i < res.Length; i++)
            {
                if (res[i].Width == this.GraphicsDevice.Viewport.Width && res[i].Height == this.GraphicsDevice.Viewport.Height)
                {
                    currentRes = i;
                    break;
                }
            }
            SetMenuEntryText();
        }

        /// <summary>
        /// Help method to set menu entries text
        /// </summary>
        void SetMenuEntryText()
        {
            if (res != null)
            {
                resolutionMenuEntry.Text = "Resolution: " + res[currentRes].Width.ToString() + "x" + res[currentRes].Height.ToString();
            }
            else
            {
                resolutionMenuEntry.Text = "Resolution: " + graphics.PreferredBackBufferWidth.ToString() + "x" + graphics.PreferredBackBufferHeight.ToString();
            }
            if (graphics.IsFullScreen == false)
            {
                fullScreenMenuEntry.Text = "Switch to fullscreen";
            }
            else
            {
                fullScreenMenuEntry.Text = "Switch to windowed";
            }
            musicVolumeMenuEntry.Text = "Music volume: " + Game1.musicVolume;
            effectsVolumeMenuEntry.Text = "Effects volume: " + Game1.effectsVolume;
        }


        /// <summary>
        /// Update method
        /// </summary>
        public override void Update(GameTime gameTime)
        {
            input.Update();
            PlayerIndex playerIndex = PlayerIndex.One;

            if (ib.menuLeft.Evaluate(input, null, out playerIndex))
            {
                if (selectedEntry == 0)
                {
                    selectedArrow = 0;
                    menuArrows[selectedArrow].OnSelectEntry(playerIndex);
                }
                if (selectedEntry == menuEntries.Count - 3)
                {
                    selectedArrow = 2;
                    menuArrows[selectedArrow].OnSelectEntry(playerIndex);
                }
                if (selectedEntry == menuEntries.Count - 2)
                {
                    selectedArrow = 4;
                    menuArrows[selectedArrow].OnSelectEntry(playerIndex);
                }
            }

            if (ib.menuRight.Evaluate(input, null, out playerIndex))
            {
                if (selectedEntry == 0)
                {
                    selectedArrow = 1;
                    menuArrows[selectedArrow].OnSelectEntry(playerIndex);
                }
                if (selectedEntry == menuEntries.Count - 3)
                {
                    selectedArrow = 3;
                    menuArrows[selectedArrow].OnSelectEntry(playerIndex);
                }
                if (selectedEntry == menuEntries.Count - 2)
                {
                    selectedArrow = 5;
                    menuArrows[selectedArrow].OnSelectEntry(playerIndex);
                }
            }


            if (ib.menuUp.Evaluate(input, null, out playerIndex))
            {
                selectedEntry--;
                if (selectedEntry < 0)
                    selectedEntry = menuEntries.Count - 1;
            }

            // Move to the next menu entry?
            if (ib.menuDown.Evaluate(input, null, out playerIndex))
            {
                selectedEntry++;

                if (selectedEntry >= menuEntries.Count)
                    selectedEntry = 0;
            }

            // If a menu entry is selected
            if (ib.menuSelect.Evaluate(input, null, out playerIndex))
            {
                menuEntries[selectedEntry].OnSelectEntry(playerIndex);
            }

            // Update all menu entries
            for (int i = 0; i < menuEntries.Count; i++)
            {
                bool isSelected = (i == selectedEntry);

                menuEntries[i].Update(isSelected, gameTime);
            }

            // Update all menu arrows entries
            for (int i = 0; i < menuArrows.Count; i++)
            {
                bool isSelected = (i == selectedArrow);

                menuArrows[i].Update(isSelected, gameTime);
            }


            base.Update(gameTime);
        }


        public override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Black);

            var height = GraphicsDevice.Viewport.Height;

            Vector2 position = new Vector2(0f, 370 * (float)height / 1080.0f);

            // update each menu entry's location in turn
            for (int i = 0; i < menuEntries.Count; i++)
            {
                MenuEntry menuEntry = menuEntries[i];

                // each entry is to be centered horizontally
                position.X = GraphicsDevice.Viewport.Width / 2 - menuEntry.GetWidth() / 2;

                 // set the entry's position
                menuEntry.Position = position;

                // move down for the next entry
                if (menuEntries.Count == 6)
                {
                    position.Y += 100 * (float)height / 1080.0f;
                }
                else
                {
                    position.Y += 120 * (float)height / 1080.0f;
                }

            }

            //set arrows and minus/plus positions
            leftArrow.Position = resolutionMenuEntry.Position - (new Vector2(35.0f, 0.0f));
            rightArrow.Position = resolutionMenuEntry.Position + (new Vector2(resolutionMenuEntry.GetWidth() + 35.0f, 0.0f));
            minMusicVolume.Position = musicVolumeMenuEntry.Position - (new Vector2(30.0f, 0.0f));
            maxMusicVolume.Position = musicVolumeMenuEntry.Position + (new Vector2(musicVolumeMenuEntry.GetWidth() + 30.0f, 0.0f));
            minEffectsVolume.Position = effectsVolumeMenuEntry.Position - (new Vector2(30.0f, 0.0f));
            maxEffectsVolume.Position = effectsVolumeMenuEntry.Position + (new Vector2(effectsVolumeMenuEntry.GetWidth() + 30.0f, 0.0f));
            
            //start drawing
            spriteBatch.Begin();
            spriteBatch.Draw(background, new Rectangle(0, 0, GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height), new Rectangle(0, 0, background.Width, background.Height), Color.White, 0.0f, Vector2.Zero, SpriteEffects.None, 0);

            // Draw each menu arrow in turn.
            for (int i = 0; i < menuArrows.Count; i++)
            {
                MenuEntry menuEntry = menuArrows[i];

                bool isSelected = (i == selectedArrow);

                //Need this check to scale down the font size for very low resolutions
                //Bad solution, but better than nothing
                if (GraphicsDevice.Viewport.Width > 600)
                    menuEntry.Draw(isSelected, gameTime, spriteBatch, font, 1.0f);
                else
                    menuEntry.Draw(isSelected, gameTime, spriteBatch, font, 0.5f);
            }


            // Draw each menu entry in turn.
            for (int i = 0; i < menuEntries.Count; i++)
            {
                MenuEntry menuEntry = menuEntries[i];

                bool isSelected = (i == selectedEntry);

                //Need this check to scale down the font size for very low resolutions
                //Bad solution, but better than nothing
                if (GraphicsDevice.Viewport.Width > 600)
                    menuEntry.Draw(isSelected, gameTime, spriteBatch, font, 1.0f);
                else
                    menuEntry.Draw(isSelected, gameTime, spriteBatch, font, 0.5f);
      
            }

            spriteBatch.End();
            base.Draw(gameTime);
        }
    }
}
